//
//  main.cpp
//  opencv_project
//
//  Created by 黄琛 on 2019/1/4.
//  Copyright © 2019 黄琛. All rights reserved.
//

#include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;

int main()
{
    const char *fn = "768x576.avi";
    VideoCapture cap;
    Mat source, result, gray, lastGray;  // gray, lastGray对应本帧和上一帧灰度图
    vector<Point2f> points[2];   // 对应上一帧和本帧的特征点，上一帧是给定的，本帧是预测结果
    vector<uchar> status;  // 每一特征点检测状态
    vector<float> err; // 每一特征点计算误差
    
    cap.open(fn);
    if(!cap.isOpened())
    {
        cout << "无法打开视频。请检查文件或摄像头" << endl;
        return -1;
    }
    
    for(;;)
    {
        cap >> source;
        if(source.empty())
            break;
        
        cvtColor(source, gray, COLOR_BGR2GRAY);
        // 以下是处理
        if(points[0].size() < 10) // 点数太少，重新检测特征点
        {
            goodFeaturesToTrack(gray, points[0], 500, 0.01, 20);
        }
        
        if(lastGray.empty())
            gray.copyTo(lastGray);
        
        // 使用金字塔L-K方法计算光流
        calcOpticalFlowPyrLK(lastGray, gray, points[0], points[1], status, err);
        
        // 下面是删除掉误判点
        int counter = 0;
        for(int i = 0; i < points[1].size(); i++)
        {
            double dist = norm(points[1][i] - points[0][i]);
            if(status[i] && dist >= 2.0 && dist <= 20.0) // 合理的特征追踪点
            {
                points[0][counter] = points[0][i];
                points[1][counter++] = points[1][i];
            }
        }
        
        points[0].resize(counter);
        points[1].resize(counter);
        
        // 显示特征点和运动轨迹
        source.copyTo(result);
        for(int i = 0; i < points[1].size(); i++)
        {
            line(result, points[0][i], points[1][i], Scalar(0, 0, 0xff));
            circle(result, points[1][i], 3, Scalar(0, 0xff, 0));
        }
        
        swap(points[0], points[1]);
        swap(lastGray, gray);
        
        imshow("源图像", source);
        imshow("检测结果", result);
        // 以下检测是否终止(按下esc终止，对应ASCII 27)
        char key = waitKey(100);
        if(key == 27)
            break;
    }
    
    waitKey(0);
    
    return 0;
}
